home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / batchut / ask11c.zip / ASK.ASM next >
Encoding:
Assembly Source File  |  1994-03-08  |  45.4 KB  |  1,225 lines

  1. ;--------------------------------------------------------------------------;
  2. ;  Program:    Ask    .Asm                                                 ;
  3. ;  Purpose:    Ask question in batch file and time out if desired.         ;
  4. ;  Notes:      Compiles under TURBO Assembler, v3.0. Should work on any    ;
  5. ;                 machine running MS-DOS, v2.xx or higher.                 ;
  6. ;  Status:     Released into the public domain. Enjoy! If you use it,      ;
  7. ;                 let me know what you think. You don't have to send       ;
  8. ;                 any money, just comments and suggestions.                ;
  9. ;  Updates:    23-Apr-90, v1.0, GAT                                        ;
  10. ;                 - initial version.                                       ;
  11. ;              13-May-90, GAT                                              ;
  12. ;                 - fixed problem with return code if time-out reached.    ;
  13. ;              08-Jul-90, GAT                                              ;
  14. ;                 - added macros to push/pop registers.                    ;
  15. ;              28-Aug-90, v1.1a, GAT                                       ;
  16. ;                 - put equates and macros in separate files.              ;
  17. ;                 - put common routines in libs.                           ;
  18. ;              12-Oct-91, v1.1b, GAT                                       ;
  19. ;                 - revised include file names.                            ;
  20. ;                 - replaced references to Push_M and Pop_M macros with    ;
  21. ;                    calls to push and pop.                                ;
  22. ;                 - removed local stack: it's not necessary.               ;
  23. ;              03-Jul-93, v1.1c, GAT                                       ;
  24. ;                 - compiled with TASM v3.0.                               ;
  25. ;                 - version number now comes from makefile.                ;
  26. ;                 - specified ??date in lowercase.                         ;
  27. ;--------------------------------------------------------------------------;
  28.  
  29. ;--------------------------------------------------------------------------;
  30. ;  Author:     George A. Theall                                            ;
  31. ;  SnailMail:  TifaWARE                                                    ;
  32. ;              610 South 48th St                                           ;
  33. ;              Philadelphia, PA.  19143                                    ;
  34. ;              U.S.A.                                                      ;
  35. ;  E-Mail:     george@tifaware.com                                         ;
  36. ;              theall@popmail.tju.edu                                      ;
  37. ;              theall@mcneil.sas.upenn.edu                                 ;
  38. ;              george.theall@satalink.com                                  ;
  39. ;--------------------------------------------------------------------------;
  40.  
  41. ;--------------------------------------------------------------------------;
  42. ;                          D I R E C T I V E S                             ;
  43. ;--------------------------------------------------------------------------;
  44. DOSSEG
  45. MODEL                tiny
  46.  
  47. IDEAL
  48. LOCALS
  49. JUMPS
  50.  
  51. FALSE               EQU       0
  52. TRUE                EQU       NOT FALSE
  53. BELL                EQU       7
  54. BS                  EQU       8
  55. TAB                 EQU       9
  56. CR                  EQU       13
  57. LF                  EQU       10
  58. ESCAPE              EQU       27             ; nb: ESC is a TASM keyword
  59. SPACE               EQU       ' '
  60. KEY_F1              EQU       3bh
  61. KEY_F2              EQU       3ch
  62. KEY_F3              EQU       3dh
  63. KEY_F4              EQU       3eh
  64. KEY_F5              EQU       3fh
  65. KEY_F6              EQU       40h
  66. KEY_F7              EQU       41h
  67. KEY_F8              EQU       42h
  68. KEY_F9              EQU       43h
  69. KEY_F10             EQU       44h
  70. KEY_HOME            EQU       47h
  71. KEY_UP              EQU       48h
  72. KEY_PGUP            EQU       49h
  73. KEY_LEFT            EQU       4bh
  74. KEY_RIGHT           EQU       4dh
  75. KEY_END             EQU       4fh
  76. KEY_DOWN            EQU       50h
  77. KEY_PGDN            EQU       51h
  78. KEY_INS             EQU       52h
  79. KEY_DEL             EQU       53h
  80. KEY_C_F1            EQU       5eh
  81. KEY_C_F2            EQU       5fh
  82. KEY_C_F3            EQU       60h
  83. KEY_C_F4            EQU       61h
  84. KEY_C_F5            EQU       62h
  85. KEY_C_F6            EQU       63h
  86. KEY_C_F7            EQU       64h
  87. KEY_C_F8            EQU       65h
  88. KEY_C_F9            EQU       66h
  89. KEY_C_F10           EQU       67h
  90. KEY_C_LEFT          EQU       73h
  91. KEY_C_RIGHT         EQU       74h
  92. KEY_C_END           EQU       75h
  93. KEY_C_PGDN          EQU       76h
  94. KEY_C_HOME          EQU       77h
  95. KEY_C_PGUP          EQU       84h
  96. KEY_F11             EQU       85h
  97. KEY_F12             EQU       86h
  98. KEY_C_F11           EQU       89h
  99. KEY_C_F12           EQU       8ah
  100. @16BIT              EQU       (@Cpu AND 8) EQ 0
  101. @32BIT              EQU       (@Cpu AND 8)
  102. NOWARN RES
  103. MACRO    PUSHA                               ;; Pushs all registers
  104.    IF @Cpu AND 2                             ;;  if for 80186 or better
  105.       pusha                                  ;;   use regular opcode
  106.    ELSE                                      ;;  else
  107.       push ax cx dx bx sp bp si di           ;;   nb: order matters!
  108.                                              ;;   nb: SP is not original!
  109.    ENDIF
  110. ENDM
  111. MACRO    POPA                                ;; Pops all registers
  112.    IF @Cpu AND 2                             ;;  if for 80186 or better
  113.       popa                                   ;;   use regular opcode
  114.    ELSE                                      ;;  else
  115.       pop di si bp bx bx dx cx ax            ;;   nb: order matters!
  116.                                              ;;   nb: don't pop SP!
  117.    ENDIF
  118. ENDM
  119. NOWARN RES
  120. MACRO    ZERO     RegList                    ;; Zeros registers
  121.    IRP      Reg, <RegList>
  122.          xor      Reg, Reg
  123.    ENDM
  124. ENDM
  125.  
  126. DOS                 EQU       21h            ; main MSDOS interrupt
  127. STDIN               EQU       0              ; standard input
  128. STDOUT              EQU       1              ; standard output
  129. STDERR              EQU       2              ; error output
  130. STDAUX              EQU       3              ; COM port
  131. STDPRN              EQU       4              ; printer
  132. TSRMAGIC            EQU       424bh          ; magic number
  133. STRUC     ISR
  134.           Entry     DW        10EBh          ; short jump ahead 16 bytes
  135.           OldISR    DD        ?              ; next ISR in chain
  136.           Sig       DW        TSRMAGIC       ; magic number
  137.           EOIFlag   DB        ?              ; 0 (80) if soft(hard)ware int
  138.           Reset     DW        ?              ; short jump to hardware reset
  139.           Reserved  DB        7 dup (0)
  140. ENDS
  141. STRUC     ISRHOOK
  142.           Vector    DB        ?              ; vector hooked
  143.           Entry     DW        ?              ; offset of TSR entry point
  144. ENDS
  145. STRUC     TSRSIG
  146.           Company   DB        8 dup (" ")    ; blank-padded company name
  147.           Product   DB        8 dup (" ")    ; blank-padded product name
  148.           Desc      DB        64 dup (0)     ; ASCIIZ product description
  149. ENDS
  150. GLOBAL at : PROC
  151. GLOBAL errmsg : PROC
  152.    GLOBAL ProgName : BYTE                    ; needed for errmsg()
  153.    GLOBAL EOL : BYTE                         ; ditto
  154. GLOBAL fgetc : PROC
  155. GLOBAL fputc : PROC
  156. GLOBAL fputs : PROC
  157. GLOBAL getchar : PROC
  158. GLOBAL getdate : PROC
  159. GLOBAL getswtch : PROC
  160. GLOBAL gettime : PROC
  161. GLOBAL getvdos : PROC
  162. GLOBAL getvect : PROC
  163. GLOBAL isatty : PROC
  164. GLOBAL kbhit : PROC
  165. GLOBAL pause : PROC
  166. GLOBAL putchar : PROC
  167. GLOBAL setvect : PROC
  168. GLOBAL sleep : PROC
  169. GLOBAL find_NextISR : PROC
  170. GLOBAL find_PrevISR : PROC
  171. GLOBAL hook_ISR : PROC
  172. GLOBAL unhook_ISR : PROC
  173. GLOBAL free_Env : PROC
  174. GLOBAL fake_Env : PROC
  175. GLOBAL check_ifInstalled : PROC
  176. GLOBAL install_TSR : PROC
  177. GLOBAL remove_TSR : PROC
  178.  
  179. GLOBAL atoi : PROC
  180. GLOBAL atou : PROC
  181. GLOBAL utoa : PROC
  182.  
  183. EOS                 EQU       0              ; terminates strings
  184. GLOBAL isalpha : PROC
  185. GLOBAL isdigit : PROC
  186. GLOBAL islower : PROC
  187. GLOBAL isupper : PROC
  188. GLOBAL iswhite : PROC
  189. GLOBAL memcmp : PROC
  190. GLOBAL strchr : PROC
  191. GLOBAL strcmp : PROC
  192. GLOBAL strlen : PROC
  193. GLOBAL tolower : PROC
  194. GLOBAL toupper : PROC
  195.  
  196.  
  197. ERRH      equ       255                      ; errorlevel if help given
  198.  
  199.  
  200. ;--------------------------------------------------------------------------;
  201. ;                        C O D E    S E G M E N T                          ;
  202. ;--------------------------------------------------------------------------;
  203. CODESEG
  204.  
  205. ORG       80h                                ; commandline
  206. LABEL     CmdLen    BYTE
  207.           db        ?
  208. LABEL     CmdLine   BYTE
  209.           db        127 dup (?)
  210.  
  211. ORG       100h                               ; start of .COM file
  212. STARTUPCODE
  213.           jmp       main                     ; skip over data and stack
  214.  
  215. ;--------------------------------------------------------------------------;
  216. ;                               D A T A                                    ;
  217. ;--------------------------------------------------------------------------;
  218. LABEL     ProgName  BYTE
  219.           db        'ask: ', EOS
  220. LABEL     EOL       BYTE
  221.           db        '.', CR, LF, EOS
  222. LABEL     HelpMsg   BYTE
  223.           db        CR, LF
  224.           db        'TifaWARE ASK, v', VERS_STR, ', ', ??date
  225.           db        ' - ask questions in batch files.', CR, LF
  226.           db        'Usage: ask [-options] [msgtxt]', CR, LF, LF
  227.           db        'Options:', CR, LF
  228.           db        '  -l  = convert response to lower case', CR, LF
  229.           db        '  -tn = wait n seconds before timing out', CR, LF
  230.           db        '  -u  = convert response to upper case', CR, LF
  231.           db        '  -?  = display this help message', CR, LF, LF
  232.           db        'msgtxt is an optional message to display.', CR, LF, EOS
  233. LABEL     Err1Msg   BYTE
  234.           db        'illegal option -- '
  235. LABEL     OptCh     BYTE
  236.           db        ?
  237.           db        EOS
  238. LABEL     Err2Msg   BYTE
  239.           db        'time-out value not specified', EOS
  240. LABEL     Err3Msg   BYTE
  241.           db        'time-out value too large -- ', EOS
  242.  
  243. SwitCh    db        '-'                      ; char introducing options
  244. HFlag     db        0                        ; flag for on-line help
  245. LFlag     db        0                        ; flag for lowercase response
  246. TFlag     db        0                        ; flag for time-out
  247. UFlag     db        0                        ; flag for uppercase response
  248. Delay     dw        ?                        ; time to pause for key
  249. MsgLen    db        0                        ; length of message text
  250. MsgTxt    dw        ?                        ; near pointer to message text
  251. RCode     db        0                        ; program return code
  252.  
  253.  
  254. ;--------------------------------------------------------------------------;
  255. ;                           P R O C E D U R E S                            ;
  256. ;--------------------------------------------------------------------------;
  257. ;----  skip_Spaces  -------------------------------------------------------;
  258. ;  Purpose:    Skips past spaces in a string.                              ;
  259. ;  Notes:      Scanning stops with either a non-space *OR* CX = 0.         ;
  260. ;  Entry:      DS:SI = start of string to scan.                            ;
  261. ;  Exit:       AL = next non-space character,                              ;
  262. ;              CX is adjusted as necessary,                                ;
  263. ;              DS:SI = pointer to next non-space.                          ;
  264. ;  Calls:      none                                                        ;
  265. ;  Changes:    AL, CX, SI                                                  ;
  266. ;--------------------------------------------------------------------------;
  267. PROC skip_Spaces
  268.  
  269.           jcxz      SHORT @@Fin
  270. @@NextCh:
  271.           lodsb
  272.           cmp       al, ' '
  273.           loopz     @@NextCh
  274.           jz        SHORT @@Fin              ; CX = 0; don't adjust
  275.  
  276.           inc       cx                       ; adjust counters if cx > 0
  277.           dec       si
  278.  
  279. @@Fin:
  280.           ret
  281. ENDP skip_Spaces
  282.  
  283.  
  284. ;----  get_Opt  -----------------------------------------------------------;
  285. ;  Purpose:    Get a commandline option.                                   ;
  286. ;  Notes:      none                                                        ;
  287. ;  Entry:      AL = option character,                                      ;
  288. ;              CX = count of characters left in commandline,               ;
  289. ;              DS:SI = pointer to first option to process.                 ;
  290. ;  Exit:       CX = count of characters left _after_ processing,           ;
  291. ;              DS:SI = pointer to whitespace _after_ options,              ;
  292. ;  Calls:      tolower, errmsg, isdigit, atou, fputs                       ;
  293. ;  Changes:    AX, BL, CX, DX, SI,                                         ;
  294. ;              [OptCh], [HFlag], [LFlag], [TFlag], [UFlag], [Delay]        ;
  295. ;--------------------------------------------------------------------------;
  296. PROC get_Opt
  297.  
  298.           mov       [OptCh], al              ; save for later
  299.           call      tolower                  ; use only lowercase in cmp.
  300.           cmp       al, 'l'
  301.           jz        SHORT @@OptL
  302.           cmp       al, 't'
  303.           jz        SHORT @@OptT
  304.           cmp       al, 'u'
  305.           jz        SHORT @@OptU
  306.           cmp       al, '?'
  307.           jz        SHORT @@OptH
  308.           mov       dx, OFFSET Err1Msg       ; unrecognized option
  309.           call      errmsg                   ; then *** DROP THRU *** to OptH
  310.  
  311. ;
  312. ; Various possible options.
  313. ;
  314. @@OptH:
  315.           mov       [HFlag], 1               ; set help flag
  316.           jmp       SHORT @@Fin
  317.  
  318. @@OptL:
  319.           mov       [LFlag], 1               ; set lowercase flag
  320.           jmp       SHORT @@Fin
  321.  
  322. @@OptT:
  323.           mov       [TFlag], 1               ; set time-out flag
  324.           mov       al, [BYTE PTR si]        ; get next character
  325.           call      isdigit                  ; if not a digit, trouble!
  326.           jz        SHORT @@GetDelay
  327.  
  328.           mov       dx, OFFSET Err2Msg       ; no delay specified
  329.           call      errmsg
  330.           jmp       @@OptH
  331.  
  332. @@GetDelay:
  333.           mov       dx, si                   ; save to adjust CX and if error
  334.           call      atou
  335.           pushf                              ; preserve flags
  336.           add       cx, dx                   ; adjust counter
  337.           sub       cx, si
  338.           popf                               ; restore flags
  339.           jc        SHORT @@BadDelay         ; error in conversion?
  340.           cmp       ax, 60*60*12             ; 12 or more hours?
  341.           jae       SHORT @@BadDelay         ;   yes, bad delay
  342.           mov       [Delay], ax
  343.           jmp       SHORT @@Fin
  344.  
  345. @@BadDelay:
  346.           push      dx
  347.           mov       bx, STDERR
  348.           mov       dx, OFFSET ProgName
  349.           call      fputs
  350.           mov       dx, OFFSET Err3Msg
  351.           call      fputs
  352.           pop       dx
  353.           mov       al, [si]                 ; save next non-digit
  354.           mov       [BYTE PTR si], EOS       ; replace with EOS
  355.           call      fputs
  356.           mov       [si], al                 ; restore it
  357.           mov       dx, OFFSET EOL
  358.           call      fputs
  359.           jmp       SHORT @@OptH
  360.  
  361. @@OptU:
  362.           mov       [UFlag], 1               ; set uppercase flag
  363.  
  364. @@Fin:
  365.           ret
  366. ENDP get_Opt
  367.  
  368.  
  369. ;----  get_Arg  -----------------------------------------------------------;
  370. ;  Purpose:    Gets a non-option from the set of commandline arguments.    ;
  371. ;  Notes:      Anything left on the commandline is user's message text.    ;
  372. ;  Entry:      CX = count of characters left in commandline,               ;
  373. ;              DS:SI = pointer to argument to process.                     ;
  374. ;  Exit:       CX = zero                                                   ;
  375. ;              DS:SI = points to CR after commandline.                     ;
  376. ;  Calls:      none                                                        ;
  377. ;  Changes:    CX, SI                                                      ;
  378. ;              [MsgLen], [MsgTxt]                                          ;
  379. ;--------------------------------------------------------------------------;
  380. PROC get_Arg
  381.  
  382.           mov       [MsgLen], cl             ; for safekeeping
  383.           mov       [MsgTxt], si
  384.           add       si, cx                   ; adjust so nothing's left
  385.           ZERO      cl
  386.           mov       [BYTE PTR si], EOS       ; finish off string
  387.  
  388.           ret
  389. ENDP get_Arg
  390.  
  391.  
  392. ;----  process_CmdLine  ---------------------------------------------------;
  393. ;  Purpose:    Processes commandline arguments.                            ;
  394. ;  Notes:      A switch character by itself is ignored.                    ;
  395. ;              No arguments whatsoever causes help flag to be set.         ;
  396. ;  Entry:      n/a                                                         ;
  397. ;  Exit:       n/a                                                         ;
  398. ;  Calls:      skip_Spaces, get_Opt, get_Arg                               ;
  399. ;  Changes:    AX, CX, SI,                                                 ;
  400. ;              BL, DX (get_Opt),                                           ;
  401. ;              [HFlag],                                                    ;
  402. ;              [OptCh], [LFlag], [TFlag], [UFlag], [Delay] (get_Opt),      ;
  403. ;              [MsgLen], [MsgTxt] (get_Arg),                               ;
  404. ;              Direction flag is cleared.                                  ;
  405. ;--------------------------------------------------------------------------;
  406. PROC process_CmdLine
  407.  
  408.           cld                                ; forward, march!
  409.           ZERO      ch, ch
  410.           mov       cl, [CmdLen]             ; length of commandline
  411.           mov       si, OFFSET CmdLine       ; offset to start of commandline
  412.  
  413.           call      skip_Spaces              ; check if any args supplied
  414.           or        cl, cl
  415.           jnz       SHORT @@ArgLoop
  416.           mov       [HFlag], 1               ; if none, set help flag
  417.           jmp       SHORT @@Fin
  418.  
  419. ;
  420. ; For each blank-delineated argument on the commandline...
  421. ;
  422. @@ArgLoop:
  423.           lodsb                              ; next character
  424.           dec       cl
  425.           cmp       al, [SwitCh]             ; is it the switch character?
  426.           jnz       SHORT @@NonOpt           ;   no
  427.  
  428. ;
  429. ; Isolate each option and process it. Stop when a space is reached.
  430. ;
  431. @@OptLoop:
  432.           jcxz      SHORT @@Fin              ; abort if nothing left
  433.           lodsb
  434.           dec       cl
  435.           cmp       al, ' '
  436.           jz        SHORT @@NextArg          ; abort when space reached
  437.           call      get_Opt
  438.           jmp       @@OptLoop
  439.  
  440. ;
  441. ; Process the current argument, which is *not* an option.
  442. ; Then, *drop thru* to advance to next argument.
  443. ;
  444. @@NonOpt:
  445.           dec       si                       ; back up one character
  446.           inc       cl
  447.           call      get_Arg
  448.  
  449. ;
  450. ; Skip over spaces until next argument is reached.
  451. ;
  452. @@NextArg:
  453.           call      skip_Spaces
  454.           or        cl, cl
  455.           jnz       @@ArgLoop
  456.  
  457. @@Fin:
  458.           ret
  459. ENDP process_CmdLine
  460.  
  461.  
  462. ;--------------------------------------------------------------------------;
  463. ;                         E N T R Y   P O I N T                            ;
  464. ;--------------------------------------------------------------------------;
  465. ;----  main  --------------------------------------------------------------;
  466. ;  Purpose:    Main section of program.                                    ;
  467. ;  Notes:      none                                                        ;
  468. ;  Entry:      Arguments as desired                                        ;
  469. ;  Exit:       Return code as follows:                                     ;
  470. ;                   0   => program timed-out                               ;
  471. ;                   255 => on-line help requested                          ;
  472. ;                   else => ASCII value of character pressed.              ;
  473. ;  Calls:      process_CmdLine, fputs, pause, getchar, tolower, toupper    ;
  474. ;  Changes:    n/a                                                         ;
  475. ;--------------------------------------------------------------------------;
  476. main:
  477.  
  478. ;
  479. ; Process commandline arguments. If the variable HFlag is set, then
  480. ; on-line help is displayed and the program immediately terminates.
  481. ;
  482.           call      process_CmdLine          ; process commandline args
  483.  
  484.           cmp       [HFlag], 0               ; is help needed?
  485.           jz        SHORT @@NoHelp           ;   no
  486.           mov       al, ERRH                 ;   yes, so set return code
  487.           mov       bx, STDERR
  488.           mov       dx, OFFSET HelpMsg       ;     point to help message
  489.           call      fputs
  490.           jmp       SHORT @@Fin              ;     and jump to end of program
  491.  
  492. ;
  493. ; Display any message from commandline then get keypress from user.
  494. ;
  495. @@NoHelp:
  496.           mov       bx, STDOUT               ; everything to stdout
  497.           cmp       [MsgLen], 0              ; anything to print out?
  498.           jz        SHORT @@NoPrompt         ;   nope
  499.           mov       dx, [MsgTxt]
  500.           call      fputs
  501.  
  502. @@NoPrompt:
  503.           cmp       [TFlag], 0               ; need to wait?
  504.           jz        SHORT @@KeyIn            ;   no
  505.           mov       ax, [Delay]              ;   yes, so...
  506.           call      pause                    ;     pause
  507.           jz        SHORT @@KeyIn            ;     zf means a key is ready
  508.           ZERO      al                       ;     set return code to zero
  509.           jmp       SHORT @@NewLine
  510.  
  511. @@KeyIn:
  512.           call      getchar
  513.  
  514. ;
  515. ; Convert character in AL as necessary. NB: if both '-l' and '-u' options
  516. ; are specified, the return value will be based on *uppercase* value.
  517. ;
  518.           cmp       [LFlag], 0               ; convert to lowercase?
  519.           jz        SHORT @@MaybeUpper       ;   no
  520.           call      tolower
  521.  
  522. @@MaybeUpper:
  523.           cmp       [UFlag], 0               ; convert to uppercase?
  524.           jz        SHORT @@NewLine          ;   no
  525.           call      toupper
  526.  
  527. ;
  528. ; Add a final newline to keep things neat.
  529. ;
  530. @@NewLine:
  531.           mov       dx, OFFSET EOL + 1
  532.           call      fputs
  533.  
  534. ;
  535. ; Ok, let's terminate the program. Return code is already in AL.
  536. ;
  537. @@Fin:
  538.           mov       ah, 4ch
  539.           int       DOS
  540.  
  541. EVEN
  542. Buffer   db    ?                          ; space for single character
  543.                                           ; nb: shared by fgetc() & fputc()
  544.  
  545.  
  546. ;-------------------------------------------------------------------------;
  547. ;  Purpose:    Reads a character from specified device.
  548. ;  Notes:      No checks are done on BX's validity.
  549. ;              Buffer is shared by fputc(). Do *NOT* use in a 
  550. ;                 multitasking environment like DESQview.
  551. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  552. ;  Entry:      BX = device handle.
  553. ;  Exit:       AL = character,
  554. ;              Carry flag set on error (AX holds error code).
  555. ;  Calls:      none
  556. ;  Changes:    AX
  557. ;              flags
  558. ;-------------------------------------------------------------------------;
  559. PROC fgetc
  560.  
  561.    push     cx dx
  562. IF @DataSize NE 0
  563.    push     ds
  564.    mov      ax, @data
  565.    mov      ds, ax
  566. ENDIF
  567.  
  568.    mov      dx, OFFSET Buffer             ; point to storage
  569.    mov      cx, 1                         ; only need 1 char
  570.    mov      ah, 3fh
  571.    int      DOS                           ; get it
  572.    jc       SHORT @@Fin                   ; abort on error
  573.    mov      al, [Buffer]
  574.  
  575. @@Fin:
  576. IF @DataSize NE 0
  577.    pop      ds
  578. ENDIF
  579.    pop      dx cx
  580.    ret
  581.  
  582. ENDP fgetc
  583.  
  584.  
  585. ;-------------------------------------------------------------------------;
  586. ;  Purpose:    Writes a character to specified device.
  587. ;  Notes:      No checks are done on BX's validity.
  588. ;              Buffer is shared by fputc(). Do *NOT* use in a 
  589. ;                 multitasking environment like DESQview.
  590. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  591. ;  Entry:      AL = character to display,
  592. ;              BX = device handle.
  593. ;  Exit:       AL = 1 if successful,
  594. ;              Carry flag set on error (AX holds error code).
  595. ;  Calls:      none
  596. ;  Changes:    AX
  597. ;-------------------------------------------------------------------------;
  598. PROC fputc
  599.  
  600.    push     cx dx
  601. IF @DataSize NE 0
  602.    push     ds
  603.    mov      dx, @data
  604.    mov      ds, ax
  605. ENDIF
  606.  
  607.    mov      dx, OFFSET Buffer             ; point to storage
  608.    mov      [Buffer], al                  ; save char
  609.    mov      cx, 1                         ; only write 1 char
  610.    mov      ah, 40h
  611.    int      DOS
  612.  
  613. IF @DataSize NE 0
  614.    pop      ds
  615. ENDIF
  616.    pop      dx cx
  617.    ret
  618.  
  619. ENDP fputc
  620.  
  621.  
  622. ;-------------------------------------------------------------------------;
  623. ;  Purpose:    Reads a character from STDIN.
  624. ;  Notes:      Character is echoed to display.
  625. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  626. ;  Entry:      n/a
  627. ;  Exit:       AL = character.
  628. ;  Calls:      none
  629. ;  Changes:    AX
  630. ;-------------------------------------------------------------------------;
  631. PROC getchar
  632.  
  633.    mov      ah, 1
  634.    int      DOS
  635.    ret
  636.  
  637. ENDP getchar
  638.  
  639.  
  640. ;-------------------------------------------------------------------------;
  641. ;  Purpose:    Writes a character to STDOUT device.
  642. ;  Notes:      none
  643. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  644. ;  Entry:      DL = character to display.
  645. ;  Exit:       n/a
  646. ;  Calls:      none
  647. ;  Changes:    none
  648. ;-------------------------------------------------------------------------;
  649. PROC putchar
  650.  
  651.    push     ax
  652.    mov      ah, 2
  653.    int      DOS
  654.    pop      ax
  655.    ret
  656.  
  657. ENDP putchar
  658.  
  659.  
  660. ;-------------------------------------------------------------------------;
  661. ;  Purpose:    Checks if a character is ready for input from STDIN.
  662. ;  Notes:      none
  663. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  664. ;  Entry:      n/a
  665. ;  Exit:       zf = 1 if character available.
  666. ;  Calls:      none
  667. ;  Changes:    flags
  668. ;-------------------------------------------------------------------------;
  669. PROC kbhit
  670.  
  671.    push     ax
  672.    mov      ah, 0bh
  673.    int      DOS
  674.    cmp      al, 0ffh                      ; AL = FFh if character ready
  675.    pop      ax
  676.    ret
  677.  
  678. ENDP kbhit
  679.  
  680.  
  681. EVEN
  682. ;-------------------------------------------------------------------------;
  683. ;  Purpose:    Writes an ASCIIZ string to specified device.
  684. ;  Notes:      A zero-length string doesn't seem to cause problems when
  685. ;                 this output function is used.
  686. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  687. ;  Entry:      BX = device handle,
  688. ;              DS:DX = pointer to string.
  689. ;  Exit:       Carry flag set if EOS wasn't found or handle is invalid.
  690. ;  Calls:      strlen
  691. ;  Changes:    none
  692. ;-------------------------------------------------------------------------;
  693. PROC fputs
  694.  
  695.    push     ax cx di es
  696.    mov      ax, ds
  697.    mov      es, ax
  698.    mov      di, dx
  699.    call     strlen                        ; set CX = length of string
  700.    jc       SHORT @@Fin                   ; abort if problem finding end
  701.    mov      ah, 40h                       ; MS-DOS raw output function
  702.    int      DOS
  703. @@Fin:
  704.    pop      es di cx ax
  705.    ret
  706.  
  707. ENDP fputs
  708.  
  709.  
  710. EVEN
  711. ;-------------------------------------------------------------------------;
  712. ;  Purpose:    Writes an error message to stderr.
  713. ;  Notes:      none
  714. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  715. ;  Entry:      DS:DX = pointer to error message.
  716. ;  Exit:       n/a
  717. ;  Calls:      fputs
  718. ;  Changes:    none
  719. ;-------------------------------------------------------------------------;
  720. PROC errmsg
  721.  
  722.    push     bx dx
  723.    mov      bx, STDERR
  724.    mov      dx, OFFSET ProgName           ; display program name
  725.    call     fputs
  726.    pop      dx                            ; recover calling parameters
  727.    push     dx                            ; and save again to avoid change
  728.    call     fputs                         ; display error message
  729.    mov      dx, OFFSET EOL
  730.    call     fputs
  731.    pop      dx bx
  732.    ret
  733.  
  734. ENDP errmsg
  735.  
  736.  
  737. EVEN
  738. ;-------------------------------------------------------------------------;
  739. ;  Purpose:    Gets current system date, based on DOS's internal clock.
  740. ;  Notes:      none
  741. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  742. ;  Entry:      n/a
  743. ;  Exit:       AL = day of week (0 = Sunday)
  744. ;              DL = day (1 to 31)
  745. ;              DH = month (1 to 12)
  746. ;              CX = year (1980 to 2099)
  747. ;  Calls:      none
  748. ;  Changes:    AX, CX, DX
  749. ;-------------------------------------------------------------------------;
  750. PROC getdate
  751.  
  752.    mov      ah, 2ah                       ; MS-DOS get system date function
  753.    int      DOS
  754.    ret
  755.  
  756. ENDP getdate
  757.  
  758.  
  759. ;-------------------------------------------------------------------------;
  760. ;  Purpose:    Gets current system time, based on DOS's internal clock.
  761. ;  Notes:      none
  762. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  763. ;  Entry:      n/a
  764. ;  Exit:       CL = minutes (0 - 59)
  765. ;              CH = hour (0 - 23)
  766. ;              DL = hundredths of seconds (0 - 99)
  767. ;              DH = seconds (0 - 59)
  768. ;  Calls:      none
  769. ;  Changes:    CX, DX
  770. ;-------------------------------------------------------------------------;
  771. PROC gettime
  772.  
  773.    push     ax
  774.    mov      ah, 2ch                       ; MS-DOS get system time function
  775.    int      DOS
  776.    pop      ax
  777.    ret
  778.  
  779. ENDP gettime
  780.  
  781.  
  782. EVEN
  783. ;-------------------------------------------------------------------------;
  784. ;  Purpose:    Pauses execution until a specified time.
  785. ;  Notes:      If time is 12 or more hours in advance, execution aborts.
  786. ;              Range for hours is 0-23, for minutes is 0-59, etc...
  787. ;              This is as accurate as the DOS clock.
  788. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  789. ;  Entry:      AX:BX = time of day (hh.mm.ss.hs) at which to awaken.
  790. ;  Exit:       n/a
  791. ;  Calls:      gettime
  792. ;  Changes:    flags
  793. ;-------------------------------------------------------------------------;
  794. PROC at
  795.  
  796.    push     cx dx
  797.  
  798. @@TimeLoop:
  799.    call     gettime                       ; get current time
  800.    sub      cx, ax                        ; cx = current time - alarm time
  801.    jz       SHORT @@CheckSecs             ; if zero, check seconds
  802.    cmp      ah, 12                        ; adjust if alarm is a.m. ...
  803.    jae      SHORT @@TooFar?
  804.    cmp      ch, 0                         ;   and current time is p.m. ...
  805.    jl       SHORT @@TooFar?               ;      (signed comparison!!!)
  806.    sub      ch, 24                        ;   by subtracting 24 hours
  807.  
  808. @@TooFar?:
  809.    neg      cx                            ; cx = alarm time - current time
  810.    cmp      cx, 12 * 256                  ; more than 12 hours difference?
  811.    ja       SHORT @@Fin                   ;   yep (unsigned comparison!!!)
  812.    jmp      @@TimeLoop                    ;   nope (already checked if ==)
  813.  
  814. @@CheckSecs:
  815.    cmp      dx, bx                        ; wait a few more seconds?
  816.    jb       @@TimeLoop                    ;   yep
  817.    or       dl, 1                         ;   no, clear zf
  818.  
  819. @@Fin:
  820.    pop      dx cx
  821.    ret
  822.  
  823. ENDP at
  824.  
  825.  
  826. ;-------------------------------------------------------------------------;
  827. ;  Purpose:    Pauses execution for a specified number of seconds or 
  828. ;                 until a keypress is detected.
  829. ;  Notes:      Delay should be less than 12 hours (43200 seconds) to 
  830. ;                 avoid checks on date rollover yet ensure we haven't
  831. ;                 paused too long. Delay is not checked however!
  832. ;              This procedure works by adding the delay to the current
  833. ;                 time and waiting until then. If the system clock is
  834. ;                 adjusted in meantime, results are unpredictable. I
  835. ;                 tried looping and calling gettime(), but that was
  836. ;                 inaccurate due to roundoff of hundreths of secs.
  837. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  838. ;  Entry:      AX = delay time (in seconds).
  839. ;  Exit:       Zero flag set if input ready; cleared otherwise.
  840. ;  Calls:      gettime, kbhit
  841. ;  Changes:    flags
  842. ;-------------------------------------------------------------------------;
  843. PROC pause
  844.  
  845.    push     ax bx cx dx
  846.    ZERO     bx
  847.    mov      cx, 60                        ; 60 secs/min and 60 mins/hour
  848.    ZERO     dx
  849.    div      cx                            ; now ax = hours and minutes
  850.    mov      bh, dl                        ; and bh = seconds (dh == 0)
  851.    ZERO     dx
  852.    div      cx                            ; now ax = hours, dx = minutes
  853.    mov      ah, al                        ; hours
  854.    mov      al, dl                        ; minutes
  855.  
  856. ;
  857. ; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
  858. ;
  859.    call     gettime                       ; get current time
  860.    add      ax, cx                        ; compute alarm time
  861.    add      bx, dx
  862.    cmp      bh, 60                        ; too many seconds?
  863.    jb       SHORT @@CheckMins             ;   nope
  864.    sub      bh, 60                        ;   yep, adjust
  865.    inc      al
  866.  
  867. @@CheckMins:
  868.    cmp      al, 60                        ; too many minutes?
  869.    jb       SHORT @@CheckHours            ;   nope
  870.    sub      al, 60                        ;   yep, adjust
  871.    inc      ah
  872.  
  873. @@CheckHours:
  874.    cmp      ah, 24                        ; too many hours?
  875.    jb       SHORT @@TimeLoop              ;   nope
  876.    sub      ah, 24                        ;   yep, adjust
  877.  
  878. ;
  879. ; Here's the main loop. Check for both keypress and alarm time.
  880. ; NB: Because of overhead in the code it's possible to overshoot
  881. ; the alarm time by a few hundreths of a second so check for that.
  882. ;
  883. @@TimeLoop:
  884.    call     kbhit                         ; check for user input
  885.    jz       SHORT @@Fin                   ; and abort if present
  886.    call     gettime                       ; get current time
  887.    sub      cx, ax                        ; cx = current time - alarm time
  888.    jz       SHORT @@CheckSecs             ; if zero, check seconds
  889.    cmp      ah, 12                        ; adjust if alarm is a.m. ...
  890.    jae      SHORT @@TooFar?
  891.    cmp      ch, 0                         ;   and current time is p.m. ...
  892.    jl       SHORT @@TooFar?               ;      (signed comparison!!!)
  893.    sub      ch, 24                        ;   by subtracting 24 hours
  894.  
  895. @@TooFar?:
  896.    neg      cx                            ; cx = alarm time - current time
  897.    cmp      cx, 12 * 256                  ; more than 12 hours difference?
  898.    ja       SHORT @@Fin                   ;   yep (unsigned comparison!!!)
  899.    jmp      @@TimeLoop                    ;   nope (already checked if ==)
  900.  
  901. @@CheckSecs:
  902.    cmp      dx, bx                        ; wait a few more seconds?
  903.    jb       @@TimeLoop                    ;   yep
  904.    or       dl, 1                         ;   no, clear zf
  905.  
  906. @@Fin:
  907.    pop      dx cx bx ax
  908.    ret
  909.  
  910. ENDP pause
  911.  
  912.  
  913. ;-------------------------------------------------------------------------;
  914. ;  Purpose:    Pauses execution for a specified number of seconds.
  915. ;  Notes:      Delay should be less than 12 hours (43200 seconds) due
  916. ;                 to the way at() is implemented. This is not checked!
  917. ;              This procedure works by adding the delay to the current
  918. ;                 time and waiting until then. If the system clock is
  919. ;                 adjusted in meantime, results are unpredictable. I
  920. ;                 tried looping and calling gettime(), but that was
  921. ;                 inaccurate due to roundoff of hundreths of secs.
  922. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  923. ;  Entry:      AX = delay time (in seconds).
  924. ;  Exit:       n/a
  925. ;  Calls:      gettime, at
  926. ;  Changes:    none
  927. ;-------------------------------------------------------------------------;
  928. PROC sleep
  929.  
  930.    push     ax bx cx dx
  931.    ZERO     bx
  932.    mov      cx, 60                        ; 60 secs/min and 60 mins/hour
  933.    ZERO     dx
  934.    div      cx                            ; now ax = hours and minutes
  935.    mov      bh, dl                        ; and bh = seconds (dh == 0)
  936.    ZERO     dx
  937.    div      cx                            ; now ax = hours, dx = minutes
  938.    mov      ah, al                        ; hours
  939.    mov      al, dl                        ; minutes
  940.  
  941. ;
  942. ; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
  943. ;
  944.    call     gettime                       ; get current time
  945.    add      ax, cx                        ; compute alarm time
  946.    add      bx, dx
  947.    cmp      bh, 60                        ; too many seconds?
  948.    jb       SHORT @@CheckMins             ;   nope
  949.    sub      bh, 60                        ;   yep, adjust
  950.    inc      al
  951.  
  952. @@CheckMins:
  953.    cmp      al, 60                        ; too many minutes?
  954.    jb       SHORT @@CheckHours            ;   nope
  955.    sub      al, 60                        ;   yep, adjust
  956.    inc      ah
  957.  
  958. @@CheckHours:
  959.    cmp      ah, 24                        ; too many hours?
  960.    jb       SHORT @@Fin                   ;   nope
  961.    sub      ah, 24                        ;   yep, adjust
  962.  
  963. @@Fin:
  964.    call     at                            ; pause until alarm time
  965.    pop      dx cx bx ax
  966.    ret
  967.  
  968. ENDP sleep
  969.  
  970.  
  971. EVEN
  972. ;-------------------------------------------------------------------------;
  973. ;  Purpose:    Converts string of digits to an *unsigned* integer in
  974. ;              range [0, 65535].
  975. ;  Notes:      Conversion stops with first non-numeric character.
  976. ;  Requires:   8086-class CPU.
  977. ;  Entry:      DS:SI = pointer to string of digits.
  978. ;  Exit:       AX = unsigned integer (garbage if cf = 1),
  979. ;              DS:SI = pointer to first non-digit found,
  980. ;              cf = 1 if number is too big.
  981. ;  Calls:      none
  982. ;  Changes:    AX, SI
  983. ;              flags
  984. ;-------------------------------------------------------------------------;
  985. PROC atou
  986.  
  987.    push     bx cx dx                      ; DX destroyed by MUL below
  988.    ZERO     ax                            ; AX = digit to convert
  989.    ZERO     bx                            ; BX = integer word
  990.    mov      cx, 10                        ; CX = conversion factor
  991.  
  992. @@NextCh:
  993.    mov      bl, [si]                      ; get character
  994.    cmp      bl, '0'                       ; test if a digit
  995.    jb       SHORT @@Fin
  996.    cmp      bl, '9'
  997.    ja       SHORT @@Fin
  998.    inc      si                            ; bump up pointer
  999.    mul      cx                            ; multiply old result by 10
  1000.    jc       SHORT @@Overflow
  1001.    sub      bl, '0'                       ; convert digit
  1002.    add      ax, bx                        ; add current value
  1003.    jnc      @@NextCh                      ; continue unless result too big
  1004.  
  1005. @@Overflow:
  1006.    ZERO     cx                            ; denotes overflow
  1007.    jmp      @@NextCh
  1008.  
  1009. @@Fin:
  1010.    cmp      cx, 10                        ; cf = (cx != 10)
  1011.    pop      dx cx bx
  1012.    ret
  1013.  
  1014. ENDP atou
  1015.  
  1016.  
  1017. EVEN
  1018. ;-------------------------------------------------------------------------;
  1019. ;  Purpose:    Tests if character is a valid ASCII alphanumeric character.
  1020. ;  Notes:      none
  1021. ;  Requires:   8086-class CPU.
  1022. ;  Entry:      AL = character to be tested.
  1023. ;  Exit:       Zero flag set if true, cleared otherwise.
  1024. ;  Calls:      none 
  1025. ;  Changes:    flags
  1026. ;-------------------------------------------------------------------------;
  1027. PROC isalpha
  1028.  
  1029.    push     ax
  1030.    or       al, 20h                       ; lowercase it
  1031.    cmp      al, 'a'                       ; if < 'a' zf = 0
  1032.    jb       SHORT @@Fin
  1033.    cmp      al, 'z'                       ; if > 'z' zf = 0
  1034.    ja       SHORT @@Fin
  1035.    cmp      al, al                        ; set Z flag
  1036. @@Fin:
  1037.    pop      ax
  1038.    ret
  1039.  
  1040. ENDP isalpha
  1041.  
  1042.  
  1043. ;-------------------------------------------------------------------------;
  1044. ;  Purpose:    Tests if character is a valid ASCII digit.
  1045. ;  Notes:      none
  1046. ;  Requires:   8086-class CPU.
  1047. ;  Entry:      AL = character to be tested.
  1048. ;  Exit:       Zero flag set if true, cleared otherwise.
  1049. ;  Calls:      none 
  1050. ;  Changes:    flags
  1051. ;-------------------------------------------------------------------------;
  1052. PROC isdigit
  1053.  
  1054.    cmp      al, '0'                       ; if < '0' zf = 0
  1055.    jb       SHORT @@Fin
  1056.    cmp      al, '9'                       ; if > '9' zf = 0
  1057.    ja       SHORT @@Fin
  1058.    cmp      al, al                        ; set Z flag
  1059. @@Fin:
  1060.    ret
  1061.  
  1062. ENDP isdigit
  1063.  
  1064.  
  1065. ;-------------------------------------------------------------------------;
  1066. ;  Purpose:    Tests if character is lowercase.
  1067. ;  Notes:      none
  1068. ;  Requires:   8086-class CPU.
  1069. ;  Entry:      AL = character to be tested.
  1070. ;  Exit:       Zero flag set if true, cleared otherwise.
  1071. ;  Calls:      none 
  1072. ;  Changes:    flags
  1073. ;-------------------------------------------------------------------------;
  1074. PROC islower
  1075.  
  1076.    cmp      al, 'a'                       ; if < 'a' zf = 0
  1077.    jb       SHORT @@Fin
  1078.    cmp      al, 'z'                       ; if > 'z' zf = 0
  1079.    ja       SHORT @@Fin
  1080.    cmp      al, al                        ; set Z flag
  1081. @@Fin:
  1082.    ret
  1083.  
  1084. ENDP islower
  1085.  
  1086.  
  1087. ;-------------------------------------------------------------------------;
  1088. ;  Purpose:    Tests if character is uppercase.
  1089. ;  Notes:      none
  1090. ;  Requires:   8086-class CPU.
  1091. ;  Entry:      AL = character to be tested.
  1092. ;  Exit:       Zero flag set if true, cleared otherwise.
  1093. ;  Calls:      none 
  1094. ;  Changes:    flags
  1095. ;-------------------------------------------------------------------------;
  1096. PROC isupper
  1097.  
  1098.    cmp      al, 'A'                       ; if < 'A' zf = 0
  1099.    jb       SHORT @@Fin
  1100.    cmp      al, 'Z'                       ; if > 'Z' zf = 0
  1101.    ja       SHORT @@Fin
  1102.    cmp      al, al                        ; set Z flag
  1103. @@Fin:
  1104.    ret
  1105.  
  1106. ENDP isupper
  1107.  
  1108.  
  1109. ;-------------------------------------------------------------------------;
  1110. ;  Purpose:    Tests if character is an ASCII whitespace.
  1111. ;  Notes:      none
  1112. ;  Requires:   8086-class CPU.
  1113. ;  Entry:      AL = character to be tested.
  1114. ;  Exit:       Zero flag set if true, cleared otherwise.
  1115. ;  Calls:      none 
  1116. ;  Changes:    flags
  1117. ;-------------------------------------------------------------------------;
  1118. PROC iswhite
  1119.  
  1120.    cmp      al, SPACE                     ; if == SPACE then zf = 1
  1121.    jz       SHORT @@Fin
  1122.    cmp      al, TAB                       ; if == TAB then zf = 1
  1123.    jz       SHORT @@Fin
  1124.    cmp      al, LF                        ; if == LF then zf = 1
  1125.    jz       SHORT @@Fin
  1126.    cmp      al, CR                        ; if == CR then zf = 1
  1127. @@Fin:
  1128.    ret
  1129.  
  1130. ENDP iswhite
  1131.  
  1132.  
  1133. EVEN
  1134. ;-------------------------------------------------------------------------;
  1135. ;  Purpose:    Converts character to lowercase.
  1136. ;  Notes:      none
  1137. ;  Requires:   8086-class CPU.
  1138. ;  Entry:      AL = character to be converted.
  1139. ;  Exit:       AL = converted character.
  1140. ;  Calls:      none
  1141. ;  Changes:    AL
  1142. ;              flags
  1143. ;-------------------------------------------------------------------------;
  1144. PROC tolower
  1145.  
  1146.    cmp      al, 'A'                       ; if < 'A' then done
  1147.    jb       SHORT @@Fin
  1148.    cmp      al, 'Z'                       ; if > 'Z' then done
  1149.    ja       SHORT @@Fin
  1150.    or       al, 20h                       ; make it lowercase
  1151. @@Fin:
  1152.    ret
  1153.  
  1154. ENDP tolower
  1155.  
  1156.  
  1157. ;-------------------------------------------------------------------------;
  1158. ;  Purpose:    Converts character to uppercase.
  1159. ;  Notes:      none
  1160. ;  Requires:   8086-class CPU.
  1161. ;  Entry:      AL = character to be converted.
  1162. ;  Exit:       AL = converted character.
  1163. ;  Calls:      none
  1164. ;  Changes:    AL
  1165. ;              flags
  1166. ;-------------------------------------------------------------------------;
  1167. PROC toupper
  1168.  
  1169.    cmp      al, 'a'                       ; if < 'a' then done
  1170.    jb       SHORT @@Fin
  1171.    cmp      al, 'z'                       ; if > 'z' then done
  1172.    ja       SHORT @@Fin
  1173.    and      al, not 20h                   ; make it uppercase
  1174. @@Fin:
  1175.    ret
  1176.  
  1177. ENDP toupper
  1178.  
  1179.  
  1180. EVEN
  1181. ;-------------------------------------------------------------------------;
  1182. ;  Purpose:    Calculates length of an ASCIIZ string.
  1183. ;  Notes:      Terminal char is _not_ included in the count.
  1184. ;  Requires:   8086-class CPU.
  1185. ;  Entry:      ES:DI = pointer to string.
  1186. ;  Exit:       CX = length of string,
  1187. ;              cf = 0 and zf = 1 if EOS found,
  1188. ;              cf = 1 and zf = 0 if EOS not found within segment.
  1189. ;  Calls:      none
  1190. ;  Changes:    CX,
  1191. ;              flags
  1192. ;-------------------------------------------------------------------------;
  1193. PROC strlen
  1194.  
  1195.    push     ax di
  1196.    pushf
  1197.    cld                                    ; scan forward only
  1198.    mov      al, EOS                       ; character to search for
  1199.    mov      cx, di                        ; where are we now
  1200.    not      cx                            ; what's left in segment - 1
  1201.    push     cx                            ; save char count
  1202.    repne    scasb
  1203.    je       SHORT @@Done
  1204.    scasb                                  ; test final char
  1205.    dec      cx                            ; avoids trouble with "not" below
  1206.  
  1207. @@Done:
  1208.    pop      ax                            ; get original count
  1209.    sub      cx, ax                        ; subtract current count
  1210.    not      cx                            ; and invert it
  1211.    popf                                   ; restore df
  1212.    dec      di
  1213.    cmp      [BYTE PTR es:di], EOS
  1214.    je       SHORT @@Fin                   ; cf = 0 if equal
  1215.    stc                                    ; set cf => error
  1216.  
  1217. @@Fin:
  1218.    pop      di ax
  1219.    ret
  1220.  
  1221. ENDP strlen
  1222.  
  1223.  
  1224. END
  1225.